package com.example.opengldemo.mbo;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;

import android.opengl.GLES30;
import android.view.View;

import com.example.opengldemo.util.MatrixState;
import com.example.opengldemo.util.ShaderUtil;

public class BallAndCube
{	
	int mProgram;
    int muMVPMatrixHandle;
    int maPositionHandle;
    int maTexCoorHandle;
    String mVertexShader;
    String mFragmentShader;
    
    int vCount=0;
    int iCount=0;
	float yAngle = 0;
	float xAngle = 0;
	float zAngle = 0;
	
	FloatBuffer mVertexMappedBuffer;
	FloatBuffer mTexCoorMappedBuffer;

    IntBuffer mIndicesBuffer;
	
	int mVertexBufferId;
	int mTexCoorBufferId;

	int mIndicesBufferId;

	ByteBuffer vbb1;
	float[]vertices;
	float[] verticesCube;
	int[] indices;
	float[] texCoors;
    public float[] curBallForDraw;
    public float[] curBallForCal;
    float span=30;
    
	ArrayList<Float> alVertix=new ArrayList<Float>();
	ArrayList<Float> alVertix1=new ArrayList<Float>();
	ArrayList<Float> alVertixTexCoor=new ArrayList<Float>();

	ArrayList<Integer> alVertixIndice=new ArrayList<Integer>();
	
	ArrayList<Float> alVertix2=new ArrayList<Float>();
	ArrayList<Float> alVertixCube2=new ArrayList<Float>();
	ArrayList<Float> alVertixTexCoor2=new ArrayList<Float>();
	
	Object lock=new Object();
    public BallAndCube(View mv, float r)
    {

    	initYSData(r);

    	initVertexData();

    	initShader(mv);
    }
    
    public void initYSData(float r)
    {
    	final float UNIT_SIZE=0.5f; //设置单位
    	final float angleSpan=5;   //设置单位度
		float length=(float) (r*UNIT_SIZE*Math.sin(Math.toRadians(45)));
		//
		float length2=length*2;
    	for(float vAngle=90;vAngle>=-90;vAngle=vAngle-angleSpan)
        {
        	for(float hAngle=360;hAngle>0;hAngle=hAngle-angleSpan)
        	{	//
        		double xozLength=r*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));

        		float x1=(float)(xozLength*Math.cos(Math.toRadians(hAngle)));
        		float z1=(float)(xozLength*Math.sin(Math.toRadians(hAngle)));
        		float y1=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(vAngle)));

        		float x10=x1;
        		float z10=z1;
        		float y10=y1;

        		float s1=0;
        		float t1=0;

        		if(vAngle==45||vAngle==-45)
        		{
        			float x1Temp=0;float z1Temp=0;      float y1Temp=0;
        			float x10Temp=0;float z10Temp=0;	float y10Temp=0;

        			float s2=0;float t2=0;

        			float xozLengthTemp=0;
        			
        			if(vAngle==45)
        			{

        				xozLengthTemp=(float) (r*UNIT_SIZE*Math.cos(Math.toRadians(45)));
            			x1Temp=(float)(xozLengthTemp*Math.cos(Math.toRadians(hAngle)));
            			z1Temp=(float)(xozLengthTemp*Math.sin(Math.toRadians(hAngle)));
            			y1Temp=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(45)));            			
            			y10Temp=length;        
        			} else {

        				xozLengthTemp=(float) (r*UNIT_SIZE*Math.cos(Math.toRadians(-45)));
            			x1Temp=(float)(xozLengthTemp*Math.cos(Math.toRadians(hAngle)));
            			z1Temp=(float)(xozLengthTemp*Math.sin(Math.toRadians(hAngle)));
            			y1Temp=(float)(r*UNIT_SIZE*Math.sin(Math.toRadians(-45)));            			
            			y10Temp=-length;        	
        			}      
        			 
         			if(Math.abs(x1Temp)>Math.abs(z1Temp)){
        				if(x1Temp>0){
        					x10Temp=(float) xozLengthTemp;
        				}else{
        					x10Temp=(float) -xozLengthTemp;
        				}
        				z10Temp=(float) (x10Temp*Math.tan(Math.toRadians(hAngle)));
        			}else{
        				if(z1Temp>0){
        					z10Temp=(float) xozLengthTemp;
        				}else{
        					z10Temp=(float) -xozLengthTemp;
        				}
        				x10Temp=(float) (z10Temp/Math.tan(Math.toRadians(hAngle)));
        			}
        			
        			if(vAngle==45){
        				s2=0.5f+x10Temp/length2;
            			t2=0.5f+z10Temp/length2;
        			} else{
        			    s2=0.5f+x10Temp/length2;
            			t2=(-0.5f+z10Temp/length2)*-1;
        			}

        			alVertix2.add(x1Temp);alVertix2.add(y1Temp);alVertix2.add(z1Temp);

        			alVertixCube2.add(x10Temp);alVertixCube2.add(y10Temp);alVertixCube2.add(z10Temp);        			

        			alVertixTexCoor2.add(s2); alVertixTexCoor2.add(t2);        			
        		}
        		
        		if(vAngle>45)
        		{
        			if(Math.abs(x1)>Math.abs(z1)){
        				if(x1>0){
        					x10=(float) xozLength;
        				}else{
        					x10=(float) -xozLength;
        				}
        				z10=(float) (x10*Math.tan(Math.toRadians(hAngle)));
        			}else{
        				if(z1>0){
        					z10=(float) xozLength;
        				}else{
        					z10=(float) -xozLength;
        				}
        				x10=(float) (z10/Math.tan(Math.toRadians(hAngle)));
        			}
        			y10=length;
        			s1=0.5f+x10/length2;
        			t1=0.5f+z10/length2;        			
        		}
        		else  if(vAngle<(-45))
        		{
        			if(Math.abs(x1)>Math.abs(z1)){
        				if(x1>0){
        					x10=(float) xozLength;
        				}else{
        					x10=(float) -xozLength;
        				}
        				z10=(float) (x10*Math.tan(Math.toRadians(hAngle)));
        			}else{
        				if(z1>0){
        					z10=(float) xozLength;
        				}else{
        					z10=(float) -xozLength;
        				}
        				x10=(float) (z10/Math.tan(Math.toRadians(hAngle)));
        			}
        			y10=-length;
        			s1=0.5f+x10/length2;
        			t1=1-(0.5f+z10/length2);
        			
        		}
        		else{            			
        			if(Math.abs(x1)>Math.abs(z1))
            		{//x>z
            			if(x1>0){
            				x10=length;
            				z10=(float) (x10*Math.tan(Math.toRadians(hAngle)));           
            				s1=0.5f+z10/length2;
            			}else{
            				x10=-length;
            				z10=(float) (x10*Math.tan(Math.toRadians(hAngle)));           
            				s1=(-0.5f+z10/length2)*-1;
            			}    			
            			t1=1-(0.5f+(y10)/length2);
            		}else{
            			if(z1>0)
            			{
            				z10=length;
            				x10=(float)(z10/Math.tan(Math.toRadians(hAngle)));
            				s1=0.5f+x10/length2;
            			}else{
            				z10=-length;
            				x10=(float)(z10/Math.tan(Math.toRadians(hAngle)));
            				s1=-1*(-0.5f+x10/length2);
            			}
            			t1=1-(0.5f+(y10)/length2);
            		}
        		}


        		alVertix.add(x1);alVertix.add(y1);alVertix.add(z1);

        		alVertix1.add(x10);alVertix1.add(y10);alVertix1.add(z10);

        		alVertixTexCoor.add(s1); alVertixTexCoor.add(t1);        		
        	}
        	
        	if(vAngle==50||vAngle==-45)
        	{       		
        		for(int i=0;i<alVertix2.size()/3;i++)
        		{
        			alVertix.add(alVertix2.get(i*3));
        			alVertix.add(alVertix2.get(i*3+1));
        			alVertix.add(alVertix2.get(i*3+2));        			    		
        		}
        		for(int i=0;i<alVertixCube2.size()/3;i++)
        		{
        			alVertix1.add(alVertixCube2.get(i*3));
        			alVertix1.add(alVertixCube2.get(i*3+1));
        			alVertix1.add(alVertixCube2.get(i*3+2));
        		}        		
        		for(int i=0;i<alVertixTexCoor2.size()/3;i++)
        		{
        			alVertixTexCoor.add(alVertixTexCoor2.get(i*3));
        			alVertixTexCoor.add(alVertixTexCoor2.get(i*3+1));
        			alVertixTexCoor.add(alVertixTexCoor2.get(i*3+2));        			
        		}
        		
        		alVertix2.clear();
        		alVertixCube2.clear();
        		alVertixTexCoor2.clear();        
        	}	
        }    	
		// 上一步 完成顶点数据 片元顶点数据
		// 完成顶点索引
    	int w = (int) (360 / angleSpan);
    	for(int i = 0; i <(w+2); i++){
    		for(int j = 0; j < w; j++){
    			int x = i * w + j;
    			alVertixIndice.add(x);
    			alVertixIndice.add(x + w);
    			alVertixIndice.add(x + 1);
    			alVertixIndice.add(x + 1);
    			alVertixIndice.add(x + w);
    			alVertixIndice.add(x + w + 1);
    		}
    	}

    	// 获取顶点个数
        vCount=alVertix.size()/3;
        //获取顶点索引的个数
        iCount=alVertixIndice.size();
		// 创建对应的数组
        vertices=new float[vCount*3];
        verticesCube=new float[vCount*3];
		// 创建对应的数组
        texCoors=new float[alVertixTexCoor.size()];
		// 数组
        curBallForDraw=new float[vertices.length];
        curBallForCal=new float[vertices.length];
		// 顶点索引数组
        indices=new int[iCount];

    	for(int i=0;i<alVertix.size();i++)
    	{	// 复数据 顶点集合 to 顶点数组  1
    		vertices[i]=alVertix.get(i);
    	}
    	
    	for(int i=0;i<iCount;i++)
    	{	// 复数据 顶点索引集合 to 顶点索引数组
    		indices[i]=alVertixIndice.get(i);
    	}
    	
    	for(int i=0;i<alVertix1.size();i++)
    	{	// 复数据 顶点集合 to 顶点数组  2
    		verticesCube[i]=alVertix1.get(i);
    	}
    	for(int i=0;i<alVertixTexCoor.size();i++)
    	{	// 复数据 片元顶点集合 to 片元顶点数组  2
    		texCoors[i]=alVertixTexCoor.get(i);
    	}
    	
    }
    

    public void initVertexData()
    {
		//创建顶点缓冲区
    	int[] buffIds=new int[3];

    	GLES30.glGenBuffers(3, buffIds, 0);
		//顶点数据缓冲区
    	mVertexBufferId=buffIds[0];
		//片元顶点数据缓冲区
    	mTexCoorBufferId=buffIds[1];
		//顶点索引数据缓冲区
    	mIndicesBufferId=buffIds[2];

        ByteBuffer tbb = ByteBuffer.allocateDirect(texCoors.length*4);
        tbb.order(ByteOrder.nativeOrder());
        FloatBuffer mTexCoorBuffer = tbb.asFloatBuffer();
        mTexCoorBuffer.put(texCoors);
        mTexCoorBuffer.position(0);


		//绑定片元顶点缓冲区
    	GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mTexCoorBufferId);
    	//导入数据到片元顶点缓冲区
    	GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, texCoors.length*4, mTexCoorBuffer, GLES30.GL_STATIC_DRAW);

		
    	ByteBuffer ibb= ByteBuffer.allocateDirect(indices.length*4);
    	ibb.order(ByteOrder.nativeOrder());
    	mIndicesBuffer=ibb.asIntBuffer();
        mIndicesBuffer.put(indices);
        mIndicesBuffer.position(0);
		//绑定顶点索引缓冲区 导入数据到顶点索引缓冲区
        GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER,mIndicesBufferId);
        GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER, indices.length*4, mIndicesBuffer, GLES30.GL_STATIC_DRAW);
		GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER,0);

		//绑定顶点缓冲区 不导入数据先让提供一个映射缓冲区
		GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mVertexBufferId);
    	GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, vertices.length*4, null, GLES30.GL_STATIC_DRAW);  
    	vbb1=(ByteBuffer)GLES30.glMapBufferRange(
    			GLES30.GL_ARRAY_BUFFER,
    			0,
    			vertices.length*4,
    			GLES30.GL_MAP_WRITE_BIT|GLES30.GL_MAP_INVALIDATE_BUFFER_BIT
    	);
    	if(vbb1==null)
    	{
    		return;
    	}
    	vbb1.order(ByteOrder.nativeOrder());
    	mVertexMappedBuffer=vbb1.asFloatBuffer();
    	// 然后将数据传输值 创建好的映射缓冲区
    	mVertexMappedBuffer.put(vertices);
    	mVertexMappedBuffer.position(0);
    	// 解开映射绑定
    	if(GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER)==false)
    	{
    		return;
    	}

    	//解开缓冲区绑定
    	GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);
    }
  

    public void initShader(View mv)
    {

        mVertexShader= ShaderUtil.loadFromAssetsFile("mbo/vertex.sh", mv.getResources());

        mFragmentShader=ShaderUtil.loadFromAssetsFile("mbo/frag.sh", mv.getResources());

        mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);

        maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");

        maTexCoorHandle=GLES30.glGetAttribLocation(mProgram, "aTexCoor");

        muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
    }

	public void updateMapping(float[] currVertex)
	{
    	GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mVertexBufferId);
    	vbb1=(ByteBuffer)GLES30.glMapBufferRange(
    			GLES30.GL_ARRAY_BUFFER, 
    			0,
    			currVertex.length*4,
    			GLES30.GL_MAP_WRITE_BIT|GLES30.GL_MAP_INVALIDATE_BUFFER_BIT
    	);
    	if(vbb1==null)
    	{
    		return;
    	}
    	vbb1.order(ByteOrder.nativeOrder());
    	mVertexMappedBuffer=vbb1.asFloatBuffer();
    	mVertexMappedBuffer.put(currVertex)  ;
    	mVertexMappedBuffer.position(0);
    	if(GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER)==false)
    	{
    		return;
    	}    	
	}
    public void drawSelf(int texId)
    {        
    	MatrixState.rotate(xAngle, 1, 0, 0);
    	MatrixState.rotate(yAngle, 0, 1, 0);
    	MatrixState.rotate(zAngle, 0, 0, 1);

    	GLES30.glUseProgram(mProgram);

    	GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0);      	


    	GLES30.glEnableVertexAttribArray(maTexCoorHandle);

    	GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mTexCoorBufferId);

    	GLES30.glVertexAttribPointer  
    	(
    			maTexCoorHandle, 
    			2, 
    			GLES30.GL_FLOAT,
    			false,
    			2*4,
    			0
    			);
    	GLES30.glEnableVertexAttribArray(maPositionHandle);

    	GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,mVertexBufferId);

    	GLES30.glVertexAttribPointer
    	(
    			maPositionHandle,   
    			3, 
    			GLES30.GL_FLOAT,  
    			false,
    			3*4,
    			0
    			); 
    	

    	GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
    	GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId);
    	
    	synchronized(lock)
    	{
        	updateMapping(curBallForDraw);
    	}
    	GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER,mIndicesBufferId);

    	GLES30.glDrawElements(GLES30.GL_TRIANGLES, iCount, GLES30.GL_UNSIGNED_INT, 0);
    	GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER,0);

    	GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);
    }
    

    public void calVertices(int count,boolean flag)
    {
    	for(int i=0;i<vertices.length;i++)
		{
			curBallForCal[i]=insertValue(vertices[i],verticesCube[i],span,count,flag);
		}
    	synchronized(lock)
    	{
        	curBallForDraw=Arrays.copyOf(curBallForCal, curBallForCal.length);
    	}
    }
    

    public float insertValue(float start,float end,float span,int count,boolean isBallToCubeY)
	{	// count = ++ // span = 30 ;
		float result=0;
		if(isBallToCubeY)
		{
			result=start+count*(end-start)/span;
		}else{
			result=end-count*(end-start)/span;
		}
		return result;
	}
}
